package com.riteny.common.httpclient;

import com.riteny.common.log.CommonLoggerFactory;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class CommonRestTemplate {

    private static Logger logger = CommonLoggerFactory.getLogger("request");

    private static Integer connectionTimeout = 10000;

    private static Integer readTimeout = 100000;

    private static RestTemplate restTemplate;

    static {

        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setConnectTimeout(connectionTimeout);// 设置超时
        requestFactory.setReadTimeout(readTimeout);

        restTemplate = new RestTemplate(requestFactory);
    }

    public static void setConnectionTimeout(Integer connectionTimeout) {

        CommonRestTemplate.connectionTimeout = connectionTimeout;

        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setConnectTimeout(connectionTimeout);// 设置超时
        requestFactory.setReadTimeout(readTimeout);

        restTemplate = new RestTemplate(requestFactory);
    }

    public static void setReadTimeout(Integer readTimeout) {

        CommonRestTemplate.readTimeout = readTimeout;

        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setConnectTimeout(connectionTimeout);// 设置超时
        requestFactory.setReadTimeout(readTimeout);

        restTemplate = new RestTemplate(requestFactory);
    }


    private static HttpHeaders setAuthHeader(String accessToken) {

        HttpHeaders headers = new HttpHeaders();

        if (accessToken != null)
            headers.set("Authorization", "bearer " + accessToken);

        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(type);
        headers.add("Accept", MediaType.APPLICATION_JSON.toString());

        return headers;
    }

    public static JSONObject getRestAuthTemplate(String url, String accessToken, JSONObject paramJson) {
        long startTime = System.currentTimeMillis();
        try {
            logger.info("#Request : " + url + " #Method : GET #Param : " + paramJson);

            HttpEntity<Map> httpEntity = new HttpEntity<>(paramJson, setAuthHeader(accessToken));

            if (paramJson != null) {

                Set<String> keySet = paramJson.keySet();

                StringBuilder stringBuilder = new StringBuilder();

                stringBuilder.append("?");

                for (String key : keySet) {
                    if (paramJson.get(key) != null) {
                        stringBuilder.append(key).append("=").append(paramJson.get(key)).append("&");
                    }
                }

                url += stringBuilder.substring(0, stringBuilder.length() - 1);
            }

            HttpEntity<JSONObject> response = restTemplate.exchange(url, HttpMethod.GET, httpEntity, JSONObject.class);

            JSONObject responseBody = JSONObject.fromObject(response.getBody());

            long useTime = System.currentTimeMillis() - startTime;
            logger.info("#Response : " + url + " #Method : GET  #use time(ms) : " + useTime + " #Result : " + responseBody);

            return responseBody;
        } catch (Exception e) {
            long useTime = System.currentTimeMillis() - startTime;
            logger.error("#Response : " + url + " #Method : GET  #use time(ms) : " + useTime + " #Result : " + e.getMessage());
            throw e;
        }
    }

    public static JSONObject postRestAuthTemplate(String url, String accessToken, JSONObject paramJson) {

        long startTime = System.currentTimeMillis();
        try {
            logger.info("#Request : " + url + " #Method : POST #Param : " + paramJson);

            HttpEntity<Map> httpEntity = new HttpEntity<>(paramJson, setAuthHeader(accessToken));

            HttpEntity response = restTemplate.exchange(url, HttpMethod.POST, httpEntity, Object.class);

            JSONObject responseBody = JSONObject.fromObject(response.getBody());

            long useTime = System.currentTimeMillis() - startTime;
            logger.info("#Response : " + url + " #Method : POST  #use time(ms) : " + useTime + " #Result : " + responseBody);

            return responseBody;
        } catch (Exception e) {
            long useTime = System.currentTimeMillis() - startTime;
            logger.error("#Response : " + url + " #Method : GET  #use time(ms) : " + useTime + " #Result : " + e.getMessage());
            throw e;
        }

    }

    public static JSONObject putRestAuthTemplate(String url, String accessToken, JSONObject paramJson) {

        long startTime = System.currentTimeMillis();
        try {
            logger.info("#Request : " + url + " #Method : PUT #Param : " + paramJson);

            HttpEntity<Map> httpEntity = new HttpEntity<>(paramJson, setAuthHeader(accessToken));

            HttpEntity response = restTemplate.exchange(url, HttpMethod.PUT, httpEntity, Object.class);

            JSONObject responseBody = JSONObject.fromObject(response.getBody());

            long useTime = System.currentTimeMillis() - startTime;
            logger.info("#Response : " + url + " #Method : PUT #use time(ms) : " + useTime + " #Result : " + responseBody);

            return responseBody;
        } catch (Exception e) {
            long useTime = System.currentTimeMillis() - startTime;
            logger.error("#Response : " + url + " #Method : GET  #use time(ms) : " + useTime + " #Result : " + e.getMessage());
            throw e;
        }
    }

    public static JSONObject deleteRestAuthTemplate(String url, String accessToken, JSONObject paramJson) {

        long startTime = System.currentTimeMillis();
        try {
            logger.info("#Request : " + url + " #Method : DEL #Param : " + paramJson);

            HttpEntity<Map> httpEntity = new HttpEntity<>(paramJson, setAuthHeader(accessToken));

            HttpEntity response = restTemplate.exchange(url, HttpMethod.DELETE, httpEntity, Object.class);

            JSONObject responseBody = JSONObject.fromObject(response.getBody());

            long useTime = System.currentTimeMillis() - startTime;
            logger.info("#Response : " + url + " #Method : DEL #use time(ms) : " + useTime + " #Result : " + responseBody);

            return responseBody;
        } catch (Exception e) {
            long useTime = System.currentTimeMillis() - startTime;
            logger.error("#Response : " + url + " #Method : GET  #use time(ms) : " + useTime + " #Result : " + e.getMessage());
            throw e;
        }
    }


    public static JSONObject getRestAuthTemplateWithHeader(String url, JSONObject paramJson, HttpHeaderEntity httpHeaderEntity) {
        long startTime = System.currentTimeMillis();
        try {
            logger.info("#Request : " + url + " #Method : GET #Param : " + paramJson);

            HttpHeaders headers = new HttpHeaders();
            headers.set(httpHeaderEntity.getHeaderName(), httpHeaderEntity.getHeaderValue());

            MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
            headers.setContentType(type);
            headers.add("Accept", MediaType.APPLICATION_JSON.toString());


            HttpEntity<Map> httpEntity = new HttpEntity<>(paramJson, headers);

            if (paramJson != null) {

                Set<String> keySet = paramJson.keySet();

                StringBuilder stringBuilder = new StringBuilder();

                stringBuilder.append("?");

                for (String key : keySet) {
                    if (paramJson.get(key) != null) {
                        stringBuilder.append(key).append("=").append(paramJson.get(key)).append("&");
                    }
                }

                url += stringBuilder.substring(0, stringBuilder.length() - 1);
            }

            HttpEntity<JSONObject> response = restTemplate.exchange(url, HttpMethod.GET, httpEntity, JSONObject.class);

            JSONObject responseBody = JSONObject.fromObject(response.getBody());

            long useTime = System.currentTimeMillis() - startTime;
            logger.info("#Response : " + url + " #Method : GET  #use time(ms) : " + useTime + " #Result : " + responseBody);

            return responseBody;
        } catch (Exception e) {
            long useTime = System.currentTimeMillis() - startTime;
            logger.error("#Response : " + url + " #Method : GET  #use time(ms) : " + useTime + " #Result : " + e.getMessage());
            throw e;
        }
    }

    public static <T> RestResponseTemplate<T> getRestAuthTemplateWithHeader(String url, JSONObject paramJson, HttpHeaderEntity httpHeaderEntity, Class<T> responseDataType) {
        long startTime = System.currentTimeMillis();
        try {
            logger.info("#Request : " + url + " #Method : GET #Param : " + paramJson);

            HttpHeaders headers = new HttpHeaders();
            headers.set(httpHeaderEntity.getHeaderName(), httpHeaderEntity.getHeaderValue());

            MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
            headers.setContentType(type);
            headers.add("Accept", MediaType.APPLICATION_JSON.toString());


            HttpEntity<Map> httpEntity = new HttpEntity<>(paramJson, headers);

            if (paramJson != null) {

                Set<String> keySet = paramJson.keySet();

                StringBuilder stringBuilder = new StringBuilder();

                stringBuilder.append("?");

                for (String key : keySet) {
                    if (paramJson.get(key) != null) {
                        stringBuilder.append(key).append("=").append(paramJson.get(key)).append("&");
                    }
                }

                url += stringBuilder.substring(0, stringBuilder.length() - 1);
            }

            HttpEntity<JSONObject> response = restTemplate.exchange(url, HttpMethod.GET, httpEntity, JSONObject.class);

            JSONObject responseBody = JSONObject.fromObject(response.getBody());

            long useTime = System.currentTimeMillis() - startTime;
            logger.info("#Response : " + url + " #Method : GET  #use time(ms) : " + useTime + " #Result : " + responseBody);

            return parseResponseResult(responseBody, responseDataType);
        } catch (Exception e) {
            long useTime = System.currentTimeMillis() - startTime;
            logger.error("#Response : " + url + " #Method : GET  #use time(ms) : " + useTime + " #Result : " + e.getMessage());
            throw e;
        }
    }


    public static <T> JSONObject putRestAuthTemplate(String url, String accessToken, T entity) {

        JSONObject paramJson = JSONObject.fromObject(entity);

        return putRestAuthTemplate(url, accessToken, paramJson);

    }

    public static <T> JSONObject postRestAuthTemplate(String url, String accessToken, T entity) {

        JSONObject paramJson = JSONObject.fromObject(entity);

        return postRestAuthTemplate(url, accessToken, paramJson);

    }

    public static <T> JSONObject getRestAuthTemplate(String url, String accessToken, T entity) {

        JSONObject paramJson = JSONObject.fromObject(entity);

        return getRestAuthTemplate(url, accessToken, paramJson);

    }

    public static <T> JSONObject deleteRestAuthTemplate(String url, String accessToken, T entity) {

        JSONObject paramJson = JSONObject.fromObject(entity);

        return deleteRestAuthTemplate(url, accessToken, paramJson);

    }


    public static <T> RestResponseTemplate<T> getRestTemplate(String url, String accessToken, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = getRestAuthTemplate(url, accessToken, paramJson);

        return parseResponseResult(resultJson, responseDataType);
    }

    public static <T> RestResponseTemplate<T> postRestTemplate(String url, String accessToken, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = postRestAuthTemplate(url, accessToken, paramJson);

        return parseResponseResult(resultJson, responseDataType);
    }

    public static <T> RestResponseTemplate<T> putRestTemplate(String url, String accessToken, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = putRestAuthTemplate(url, accessToken, paramJson);

        return parseResponseResult(resultJson, responseDataType);
    }

    public static <T> RestResponseTemplate<T> deleteRestTemplate(String url, String accessToken, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = deleteRestAuthTemplate(url, accessToken, paramJson);

        return parseResponseResult(resultJson, responseDataType);
    }


    public static JSONObject getRestTemplate(String url, JSONObject paramJson) {
        return getRestAuthTemplate(url, null, paramJson);
    }

    public static JSONObject postRestTemplate(String url, JSONObject paramJson) {
        return postRestAuthTemplate(url, null, paramJson);
    }

    public static JSONObject putRestTemplate(String url, JSONObject paramJson) {
        return putRestAuthTemplate(url, null, paramJson);
    }

    public static JSONObject deleteRestTemplate(String url, JSONObject paramJson) {
        return deleteRestAuthTemplate(url, null, paramJson);
    }


    public static <T> RestResponseTemplate<T> getRestTemplate(String url, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = getRestAuthTemplate(url, null, paramJson);

        return parseResponseResult(resultJson, responseDataType);
    }

    public static <T> RestResponseTemplate<T> postRestTemplate(String url, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = postRestAuthTemplate(url, null, paramJson);

        return parseResponseResult(resultJson, responseDataType);
    }

    public static <T> RestResponseTemplate<T> putRestTemplate(String url, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = putRestAuthTemplate(url, null, paramJson);

        return parseResponseResult(resultJson, responseDataType);
    }

    public static <T> RestResponseTemplate<T> deleteRestTemplate(String url, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = deleteRestAuthTemplate(url, null, paramJson);

        return parseResponseResult(resultJson, responseDataType);
    }


    public static <T> List<T> getRestTemplateForList(String url, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = getRestAuthTemplate(url, null, paramJson);

        return parseResponseResultList(resultJson, responseDataType);
    }

    public static <T> List<T> postRestTemplateForList(String url, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = postRestAuthTemplate(url, null, paramJson);

        return parseResponseResultList(resultJson, responseDataType);
    }

    public static <T> List<T> putRestTemplateForList(String url, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = putRestAuthTemplate(url, null, paramJson);

        return parseResponseResultList(resultJson, responseDataType);
    }

    public static <T> List<T> delRestTemplateForList(String url, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = deleteRestAuthTemplate(url, null, paramJson);

        return parseResponseResultList(resultJson, responseDataType);
    }


    public static <T> List<T> getRestTemplateForList(String url, String accessToken, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = getRestAuthTemplate(url, accessToken, paramJson);

        return parseResponseResultList(resultJson, responseDataType);
    }

    public static <T> List<T> postRestTemplateForList(String url, String accessToken, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = postRestAuthTemplate(url, accessToken, paramJson);

        return parseResponseResultList(resultJson, responseDataType);
    }

    public static <T> List<T> putRestTemplateForList(String url, String accessToken, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = putRestAuthTemplate(url, accessToken, paramJson);

        return parseResponseResultList(resultJson, responseDataType);
    }

    public static <T> List<T> delRestTemplateForList(String url, String accessToken, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = deleteRestAuthTemplate(url, accessToken, paramJson);

        return parseResponseResultList(resultJson, responseDataType);
    }


    public static <T> HttpPageResponseEntity<List<T>> getRestTemplateForPage(String url, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = getRestAuthTemplate(url, null, paramJson);

        return parseResponseResultPage(resultJson, responseDataType);
    }

    public static <T> HttpPageResponseEntity<List<T>> getRestTemplateForPage(String url, String accessToken, JSONObject paramJson, Class<T> responseDataType) {

        JSONObject resultJson = getRestAuthTemplate(url, accessToken, paramJson);

        return parseResponseResultPage(resultJson, responseDataType);
    }


    private static <T> RestResponseTemplate<T> parseResponseResult(JSONObject resultJson, Class<T> responseDataType) {

        T data = null;

        if (!"1".equals(resultJson.opt("resultCode"))) {
            throw new CommonHttpClientException(resultJson.optString("resultCode"), resultJson.optString("resultMsg"));
        }

        if (resultJson.opt("data") != null) {
            data = (T) JSONObject.toBean(resultJson.optJSONObject("data"), responseDataType);
        }

        return new RestResponseTemplate(resultJson.optString("resultCode"), resultJson.optString("resultMsg"), data);
    }

    private static <T> List<T> parseResponseResultList(JSONObject resultJson, Class<T> responseDataType) {

        List<T> ts = new ArrayList<>();

        if (!"1".equals(resultJson.opt("resultCode"))) {
            throw new CommonHttpClientException(resultJson.optString("resultCode"), resultJson.optString("resultMsg"));
        }

        JSONArray dataJson = resultJson.optJSONArray("data");
        if (dataJson == null) {
            return ts;
        }

        dataJson.forEach(value -> {

            T data = (T) JSONObject.toBean(JSONObject.fromObject(value), responseDataType);

            ts.add(data);
        });

        return ts;
    }

    private static <T> HttpPageResponseEntity<List<T>> parseResponseResultPage(JSONObject resultJson, Class<T> responseDataType) {

        List<T> ts = new ArrayList<>();

        if (!"1".equals(resultJson.opt("resultCode"))) {
            throw new CommonHttpClientException(resultJson.optString("resultCode"), resultJson.optString("resultMsg"));
        }

        HttpPageResponseEntity<List<T>> pageResponseEntity = new HttpPageResponseEntity<>();

        JSONObject dataJson = resultJson.optJSONObject("data");
        if (dataJson == null) {
            return new HttpPageResponseEntity<>();
        }

        JSONArray jsonArray = dataJson.optJSONArray("data");
        if (jsonArray == null) {
            return new HttpPageResponseEntity<>();
        }

        jsonArray.forEach(value -> {

            T data = (T) JSONObject.toBean(JSONObject.fromObject(value), responseDataType);

            ts.add(data);
        });

        return pageResponseEntity.withTotal(dataJson.getLong("total")).withData(ts);
    }
}
